<template>
  <div class="tree-node">
    <div
      class="node-content"
      :class="{ 'has-children': hasChildren, 'drag-over': dragOverNode === node.id }"
      :draggable="true"
      :style="{
        backgroundColor: isSelected ? '#e4efff' : 'transparent',
        color: themeStyle[theme].textColor2
      }"
      @dragstart="onDragStart"
      @dragover="onDragOver"
      @drop="onDrop"
      @dragleave="onDragLeave"
      @click="toggle($event)"
      @contextmenu="toggle($event)"
    >
      <div class="node-content-left">
        <span v-if="hasChildren" class="toggle-icon" @click.stop="expandIcon">
          <AppstoreOutlined />
        </span>
        <span v-else class="toggle-icon">
          <CodeSandboxOutlined />
        </span>
        <div class="node-label">
          <DblclickEdit
            v-model:text="node[fieldNames]"
            @start-edit="dbclickStartEdit"
            @edited="emit('update-text', { id: node.id, text: $event })"
          />
        </div>
      </div>
      <div class="node-content-right" :style="{ display: node.lock ? 'block' : 'none' }">
        <LockOutlined v-if="node.lock" @click="switchLock" />
        <UnlockOutlined v-else @click="switchLock" />
      </div>
    </div>

    <div class="animationDiv" :style="{ height }">
      <template v-if="hasChildren && expandItems.includes(node.id)">
        <div :id="id" class="children">
          <div class="line" :style="{ backgroundColor: themeStyle[theme].borderColor1 }"></div>
          <tree-node
            v-for="child in node.children"
            :key="child.id"
            :node="child"
            :field-names="fieldNames"
            :level="level + 1"
            @node-click="$emit('node-click', $event)"
            @expanded="$emit('expanded', $event)"
            @switch-lock="$emit('switchLock', $event)"
            @node-drop="$emit('node-drop', $event)"
            @update-text="$emit('update-text', $event)"
          />
        </div>
      </template>
    </div>
  </div>
</template>

<script lang="ts" setup>
import {
  AppstoreOutlined,
  CodeSandboxOutlined,
  UnlockOutlined,
  LockOutlined
} from '@ant-design/icons-vue'
import { computed, ref, inject, Ref, watch } from 'vue'
import { expandItems, setExpandItems, currentExpandItem } from './hook'
import { generateUUID } from '@renderer/utils/utils'
import { useStyleStore } from '@renderer/store/globalStyle/style'
import { storeToRefs } from 'pinia'
import DblclickEdit from '../dbclickEdit/DblclickEdit.vue'

const props = defineProps({
  node: {
    type: Object,
    required: true
  },
  fieldNames: {
    type: String,
    default: 'label'
  },
  level: {
    type: Number,
    default: 0
  }
})

const node = ref(props.node)
watch(
  () => props.node,
  (val) => {
    node.value = val
  }
)

const id = ref<string>(generateUUID())

const emit = defineEmits(['node-click', 'expanded', 'switchLock', 'node-drop', 'update-text'])

// 注入选中状态
const selectedNodes = inject<Ref<Set<string>>>('selectedNodes')!

// 计算当前节点是否被选中
const isSelected = computed(() => selectedNodes.value.has(props.node.id))

const hasChildren = computed(() => {
  return props.node.children && props.node.children.length > 0
})

//延时展开
const expandTimer = ref<any>(null)

const toggle = (event: MouseEvent): void => {
  if (hasChildren.value && expandTimer.value === null) {
    expandTimer.value = setTimeout(() => {
      expandIcon()
      expandTimer.value = null
    }, 50)
  }
  if (!isSelected.value) {
    // 传递事件对象给父组件
    emit('node-click', { node: props.node, event: event })
  }
}

const dbclickStartEdit = (): void => {
  if (expandTimer.value) {
    clearTimeout(expandTimer.value)
    expandTimer.value = null
  }
}

const expandIcon = (): void => {
  currentExpandItem.value = props.node.id
  // 判断是展开还是收起
  if (expandItems.value.includes(props.node.id)) {
    // 收起
    const divElement = document.getElementById(id.value) as HTMLElement
    if (divElement) {
      // 获取divElement的高度
      const divHeight = divElement.offsetHeight
      height.value = divHeight + 'px'
      setTimeout(() => {
        height.value = '0px'
        setTimeout(() => {
          height.value = undefined
          setExpandItems(props.node.id)
        }, 300)
      }, 100)
    }
  } else {
    // 展开
    height.value = '0px'
    setTimeout(() => {
      const divElement = document.getElementById(id.value) as HTMLElement
      if (divElement) {
        // 获取divElement的高度
        const divHeight = divElement.offsetHeight
        height.value = divHeight + 'px'
        setTimeout(() => {
          height.value = undefined
        }, 300)
      }
    }, 100)
    setExpandItems(props.node.id)
  }

  emit('expanded', props.node)
}

const height = ref<undefined | string>(undefined)

const globalStyleStore = useStyleStore()
const { theme, themeStyle } = storeToRefs(globalStyleStore)

const switchLock = (e: MouseEvent): void => {
  //阻止事件冒泡
  e.stopPropagation()
  emit('switchLock', props.node.id)
}

const dragOverNode = ref<string | null>(null)

const onDragStart = (e: DragEvent) => {
  e.dataTransfer?.setData('application/node-id', props.node.id)
  e.stopPropagation()
}

const onDragOver = (e: DragEvent) => {
  e.preventDefault()
  dragOverNode.value = props.node.id
}

const onDrop = (e: DragEvent) => {
  e.preventDefault()
  const sourceId = e.dataTransfer?.getData('application/node-id')
  if (sourceId && sourceId !== props.node.id) {
    emit('node-drop', { sourceId, targetId: props.node.id })
  }
  dragOverNode.value = null
}

const onDragLeave = () => {
  dragOverNode.value = null
}
</script>

<style scoped lang="scss">
.tree-node {
  margin: 3px 0;
  margin-left: 0px;
  cursor: pointer;
  width: 100%;
  text-wrap: nowrap;

  .node-content {
    padding: 2px;
    padding-left: 5px;
    transition: background-color 0.2s;
    display: flex;
    justify-content: space-between;
    align-items: center;

    &.drag-over {
      border-top: 1px solid #409eff;
      background-color: #f0faff;
    }

    &:hover {
      .node-content-right {
        display: block !important;
      }
    }

    .node-content-left {
      display: flex;
      justify-content: left;
      align-items: center;

      .toggle-icon {
        display: inline-block;
        width: 15px;
        text-align: center;
        margin-right: 5px;
      }

      .node-label {
        width: 100%;
        user-select: none;
        cursor: pointer;
        line-height: 20px;
      }
    }
    .node-content-right {
      padding-right: 10px;
      display: none;
    }
  }
}

.animationDiv {
  transition: all 0.3s ease-in-out;
  overflow: hidden;

  .children {
    // max-height: 200px;
    width: calc(100% - 15px);
    padding-left: 15px;
    position: relative;
    overflow-y: hidden;
    overflow-x: hidden;

    .line {
      position: absolute;
      width: 1px;
      height: calc(100% - 10px);
      top: 5px;
      left: 5.5px;
    }
  }
}
</style>
